#include "functionParserWrapper.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/catch_approx.hpp>
#include <iostream>
#include <string>

using namespace hms;

using Catch::Approx;
using Catch::Matchers::ContainsSubstring;

TEST_CASE( "Parsing function" )
{
	std::string bad { "   ( 2*x + 3*y - 4*z^2 )* (1/t)" };
	/* no value for z, must still evaluate correctly */
	std::string good { "( 2*x + 3*y - 4*y^2 ) * 2^t" };
	
	SECTION( "Creating function parser first, then parsing string" )
	{
		FunctionParserWrapper fp;
		REQUIRE_THROWS_WITH(
			fp.parse(bad),
			ContainsSubstring("Division by zero")
		);

		fp.parse( good );
		REQUIRE( fp.eval( 0,0,0,1 ) == Approx(0) );
		REQUIRE( fp.eval( 0.5, 2, 0, 1 ) == Approx(-18) );

		REQUIRE_THROWS_WITH(
			fp.parse( " { 123 " ),
			ContainsSubstring("Syntax error")
		);
		REQUIRE_THROWS_WITH(
			fp.parse( " 123 } " ),
			ContainsSubstring("Operator expected")
		);
	}
	SECTION( "Creating function parser directly from string" )
	{
		FunctionParserWrapper fp { good };
		
		REQUIRE( fp.eval( 0,0,0,1 ) == Approx(0) );
		REQUIRE( fp.eval( 0.5, 2, 0, 1 ) == Approx(-18) );
		
		REQUIRE_THROWS( FunctionParserWrapper{ " { 123 " } );
	}
	SECTION( "Using automatically added constants" ){
		std::string func { "x^2 / (2*g) + pi*y^2/4" };
		FunctionParserWrapper fp (func, true);
		REQUIRE( fp.eval(3) == Approx(50./109) );
		REQUIRE( fp.eval(0, 0.1) == Approx(1./400 * math::pi) );
	}
	SECTION( "Parsing function string with extra variables" ){
		std::string extra {
			"A := x^2 / (2*g);\n"
			"rho:= 1000;"
			"B :=y / (rho*g);\t"
			"4e2 * A + B"
		};
		FunctionParserWrapper fp (extra);
		double x = 2, y = 3;
		REQUIRE( fp.eval(x,y) ==
			Approx( 400*x*x/(2*phys::g) + y/(1000*phys::g) )
		);
	}
}